package com.demo.task.socket

import android.content.Context
import android.util.Log
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import com.chtj.socket.ISocket
import com.chtj.socket.ISocketListener
import com.chtj.socket.SocketData
import java.io.IOException
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetAddress
import java.net.NetworkInterface
import java.net.SocketException
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.LinkedBlockingQueue


/**
 * @author  yyk
 * @create  2023-07-28
 */
class UdpSocket : ISocket {
    var mHost: String? = null
    var mPort = 0
    var mTimeout = 15000
    var mSocketListener: ISocketListener? = null
    var netInterface: String? = null

    private var esClient = Executors.newFixedThreadPool(3)
    private var ftReceive: Future<*>? = null
    private var ftSend: Future<*>? = null
    private val queue = LinkedBlockingQueue<SocketData>()

    private var mDatagramSocket: DatagramSocket? = null
    private val sendBuffer = ByteArray(MAX_UDP_DATAGRAM_LEN)
    private val packetSize = MAX_UDP_DATAGRAM_LEN
    private var sendTimeMillis = 0L

    constructor(mHost: String?, mPort: Int, mTimeout: Int) {
        this.mHost = mHost
        this.mPort = mPort
        this.mTimeout = mTimeout
    }

    constructor(mHost: String?, mPort: Int, mTimeout: Int, mSocketListener: ISocketListener?) {
        this.mHost = mHost
        this.mPort = mPort
        this.mTimeout = mTimeout
        this.mSocketListener = mSocketListener
    }


    override fun connect(ctx: Context?) {
        if (ftReceive == null || ftReceive!!.isDone) {
            ftReceive = esClient.submit(receiveRunnable)
        }
    }

    override fun send(data: ByteArray?) {
        data?.also {
            send(data, 0, data.size)
        }
    }

    override fun send(data: ByteArray, offset: Int, size: Int) {
        try {
            queue.put(SocketData(data, offset, size))
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
    }

    override fun close() {
        esClient.submit {
            mDatagramSocket?.close()
        }
        ftSend?.cancel(true)
        ftReceive?.cancel(true)
    }

    override fun isClosed(): Boolean {
        return ftReceive == null || ftReceive!!.isDone
    }

    override fun setSocketListener(listener: ISocketListener?) {
        this.mSocketListener = listener
    }

    private fun startSendRunnable() {
        if (ftSend == null || ftSend!!.isDone) {
            Log.e(TAG, "startSendRunnable -> connect host:$mHost\tport:$mPort")
            ftSend = esClient.submit(sendBufRunnable)
        }
    }

    private fun getNetInterfaceHost(netInterface: String?): InetAddress? {
        if (netInterface.isNullOrEmpty()) return null

        val networkInterfaces = NetworkInterface.getNetworkInterfaces()
        while (networkInterfaces.hasMoreElements()) {
            val networkInterface = networkInterfaces.nextElement()
            if (networkInterface.name == netInterface) {
                val inetAddresses = networkInterface.inetAddresses
                while (inetAddresses.hasMoreElements()) {
                    val inetAddress = inetAddresses.nextElement()
                    if (!inetAddress.isLoopbackAddress && inetAddress.address.size == 4) {
                        return inetAddress
                    }
                }
            }
        }
        return null
    }

    private val sendBufRunnable = Runnable {
        var socketData: SocketData
        try {
            val inetAddress = InetAddress.getByName(mHost)
            val datagramPacket = DatagramPacket(sendBuffer, sendBuffer.size, inetAddress, mPort)
            while (queue.take().also { socketData = it } != null) {
                val currentTimeMillis = System.currentTimeMillis()
                if (currentTimeMillis - sendTimeMillis < 1000) {
                    Thread.sleep(1000)
                }
                sendTimeMillis = currentTimeMillis

                Log.e(TAG, "send: ${mDatagramSocket?.localAddress}")
                datagramPacket.setData(socketData.data, socketData.offset, socketData.size)
                mDatagramSocket?.send(datagramPacket)
                mSocketListener?.writeSuccess(socketData.data)
            }
        } catch (e: InterruptedException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
            mSocketListener?.connFaild(e)
        }
        queue.clear()
    }

    private val receiveRunnable: Runnable = object : Runnable {
        override fun run() {
            try {
                val inetAddress = getNetInterfaceHost(netInterface!!)
                mDatagramSocket = when {
                    inetAddress != null -> DatagramSocket(mPort, inetAddress)
                    else -> DatagramSocket(mPort)
                }
                Log.e(TAG, "receiveRunnable: ${mDatagramSocket?.localAddress}")

                //连接成功
                mSocketListener?.connSuccess()
                startSendRunnable()

                val buffer = ByteArray(packetSize)
                val datagramPacket = DatagramPacket(buffer, packetSize)
                mDatagramSocket?.receive(datagramPacket)//线程阻塞

                var size = datagramPacket.length
                while (size > 0) {
                    mSocketListener?.recv(buffer, 0, size)
                    mDatagramSocket?.receive(datagramPacket)
                    size = datagramPacket.length
                }
            } catch (e: SocketException) {
                e.printStackTrace()
                mSocketListener?.connFaild(e)
            } catch (e: IOException) {
                e.printStackTrace()
                mSocketListener?.connFaild(e)
            } finally {
                close()
            }

            mSocketListener?.connClose()
            Log.e(TAG, this.toString() + "receiveRunnable end")
        }
    }

    companion object {
        private const val TAG = "UdpSocket"
        private const val MAX_UDP_DATAGRAM_LEN = 1024 * 4
    }
}
